#include "BitmapImage.h"
#include <string>
#include <exception>

using namespace std;

BitmapImage::BitmapImage(int inWidth, int inHeight)
	:mWidth(inWidth),
	 mHeight(inHeight),
	 mpData(new uint16_t[inWidth*inHeight])
{
	SetTransparent();
}

BitmapImage::BitmapImage(const BitmapImage& b)
	:mWidth(b.mWidth),
	 mHeight(b.mHeight),
	 mpData(new uint16_t[mWidth*mHeight])
{
	memcpy(mpData, b.mpData, mWidth*mHeight*sizeof(*mpData));
}

BitmapImage::BitmapImage(int inW, int inH, uint16_t* pD)
	:mWidth(inW),
	 mHeight(inH),
	 mpData(new uint16_t[inW*inH])
{
	memcpy(mpData, pD, mWidth*mHeight*sizeof(*mpData));
}

BitmapImage& BitmapImage::operator=(const BitmapImage& im)
{
	if(&im != this)
	{
		delete[] mpData;
		mpData = NULL;
		mWidth = im.mWidth;
		mHeight = im.mHeight;
		mpData = new uint16_t[mWidth*mHeight];
		memcpy(mpData, im.mpData, mWidth*mHeight*sizeof(*mpData));
	}
	return *this;
}

BitmapImage& BitmapImage::operator+=(const BitmapImage& b)
{
	DimensionCheck(b);
	for(int i=0; i<mWidth*mHeight; i++)
		mpData[i] += b.mpData[i];
	return *this;
}

BitmapImage& BitmapImage::operator-=(const BitmapImage& b)
{
	DimensionCheck(b);
	for(int i=0; i<mWidth*mHeight; i++)
		mpData[i] -= b.mpData[i];
	return *this;
}

BitmapImage& BitmapImage::SetBackground(const BitmapImage& b)
{
	DimensionCheck(b);
	for(int i=0; i<mWidth*mHeight; i++)
	{
		if(mpData[i] & 0x8000)
			mpData[i] = b.mpData[i];
	}
	return *this;
}

void BitmapImage::DimensionCheck(const BitmapImage& b) const
{
	if(mWidth != b.mWidth || mHeight != b.mHeight)
		throw exception("BitmapImage dimension mismatch");
}

ostream& BitmapImage::WriteBinary(ostream& os) const
{
	os.put(mWidth & 0xff).put(mWidth >> 8)
	  .put(mHeight& 0xff).put(mHeight>> 8);
	uint16_t* pHead = mpData,
			* pTail = mpData + mWidth*mHeight;
	while(pHead < pTail)
	{
		int value = *pHead;
		int length = 0;
		while((pHead < pTail) && (*pHead == value) && (length < 0x100))
			++pHead, ++length;
		os.put(--length & 0xff).put(value & 0xff).put(value >> 8);
	}

	return os;
}

istream& BitmapImage::ReadBinary(istream& is)
{
	mWidth = uint8_t(is.get());
	mWidth |= uint8_t(is.get()) << 8;
	mHeight = uint8_t(is.get());
	mHeight |= uint8_t(is.get()) << 8;
	delete[] mpData;
	mpData = new uint16_t[mWidth*mHeight];
	uint16_t* pHead = mpData,
			* pTail = mpData + mWidth*mHeight;
	while(pHead < pTail)
	{
		int len = uint8_t(is.get());
		int value = uint8_t(is.get());
		value |= uint8_t(is.get()) << 8;
		for(int i=0; i<len && pHead<pTail; i++)
			*pHead = value;
	}
	return is;
}